<?php

namespace tbollmeier\alteraparser\ast;


class AstNode
{
    private $name;
    public $id;
    private $children;

    public function __construct($optParams=[])
    {
        $this->name = $this->getParam($optParams, 'name', '');
        $this->id = $this->getParam($optParams, 'id', '');
        $this->children = [];

        $text = $this->getParam($optParams, 'text', '');
        if ($text) {
            array_push($this->children, new TextNode($text));
        }
    }

    public function getName()
    {
        return $this->name;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getOwnText()
    {
        $ownText = '';

        foreach ($this->children as $child) {
            if ($child instanceof TextNode) {
                $ownText .= $child->text;
            }
        }

        return $ownText;
    }

    public function getText()
    {
        $text = '';

        foreach ($this->children as $child) {
            if ($child instanceof TextNode) {
                $text .= $child->text;
            } else {
                $text .= $child->getText();
            }
        }

        return $text;
    }

    public function getChildren($nameOrId='')
    {
        if (!$nameOrId) {
            return $this->children;
        } else {
            if ($nameOrId[0] === '#') {
                $id = substr($nameOrId, 1);
                return $this->getChildrenById($id);
            } else {
                $name = $nameOrId;
                return $this->getChildrenByName($name);
            }
        }
    }

    public function getAstChildren()
    {
        $astChildren = [];

        foreach ($this->children as $child) {
            if ($child instanceof AstNode) {
                array_push($astChildren, $child);
            }
        }

        return $astChildren;
    }

    public function getChildrenByName($name, $recursive=true)
    {
        $res = [];

        foreach ($this->children as $child) {
            if (!($child instanceof AstNode)) continue;
            $childName = $child->getName();
            if ($childName == $name) {
                array_push($res, $child);
            } elseif ($recursive && !$childName) {
                $res = array_merge($res, $child->getChildrenByName($name));
            }
        }

        return $res;
    }

    public function getChildrenById($id, $recursive=true)
    {
        $res = [];

        foreach ($this->children as $child) {
            if (!($child instanceof AstNode)) continue;
            $childId = $child->getId();
            if ($childId == $id) {
                array_push($res, $child);
            } elseif ($recursive && !$childId) {
                $res = array_merge($res, $child->getChildrenById($id));
            }
        }

        return $res;
    }

    public function addChild($child, $pos=null)
    {
        if ($pos === null) {
            array_push($this->children, $child);
        } else {
            array_splice($this->children, $pos, 0, [$child]);
        }
    }

    public function toXml($indentSize=2)
    {
        $xml = '<?xml version="1.0"?>';
        return $this->toXmlInternal($xml, 0, $indentSize);
    }

    private function getParam($optParams, $key, $default)
    {
        return array_key_exists($key, $optParams) ? $optParams[$key] : $default;
    }

    private function toXmlInternal($xml, $indent, $indentSize)
    {
        $name = $this->name ? $this->name : 'ast';
        $newXml = '';
        $numBlanks = $indent * $indentSize;
        for ($i=0; $i<$numBlanks; $i++) {
            $newXml .= ' ';
        }
        $newXml .= "<{$name}";
        if ($this->id) {
            $newXml .= "id = \"{$this->id}\"";
        }
        $text = $this->getOwnText();
        $children = $this->getAstChildren();
        $closed = false;
        if ($text || count($children) > 0) {
            $newXml .= '>';
        } else {
            $newXml .= '/>';
            $closed = true;
        }
        if ($text) {
            $newXml .= $text;
        }
        if (count($children) > 0) {
            foreach ($children as $child) {
                $newXml = $child->toXmlInternal($newXml, $indent+1, $indentSize);
            }
        }
        if (!$closed) {
            if (count($children) > 0) {
                $newXml .= PHP_EOL;
                for ($i=0; $i<$numBlanks; $i++) {
                    $newXml .= ' ';
                }
            }
            $newXml .= "</{$name}>";
        }
        return $xml ? $xml . PHP_EOL . $newXml : $newXml;
    }

}